#define APP1		(uint8)'R'
#define APP2		(uint8)'L'

#ifdef USE_SQL
#include <string.h>

#include "lib.h"
#include "rlserv.h"
#include "rlconn.h"
#include "rlsql.h"
#include "rlproto.h"
#include "stdncerr.h"
#include "opts.h"
#include "log.h"
#include "badpacketmsgs.h"
#include "XMLTranslator.h"

static RouletteSQL* tracker = (RouletteSQL*)0;

typedef struct _info
{
	String			login;
	PID				pid;
	SESSIONID		sessionId;
	uint8			tableID;
	bool8			forMoney;
} Info, *pInfo;

void
RouletteServer::startTracker()
{
	ServerOpts::load();

	tracker = new RouletteSQL(ServerOpts::dbdsn,
		ServerOpts::dblogin, ServerOpts::dbpasswd);

	// CSN-8891
	webServiceCall = new WebServiceCall();
}

TrackSQL* 
RouletteServer::getTracker()
{
	return tracker;
}


void
RouletteServer::stopTracker()
{
	delete tracker;
	tracker = 0;

	delete webServiceCall;
	webServiceCall = 0;
}

bool8
RouletteServer::initSuccess()
{
	if (tracker && tracker->initOK())
		return Server::initSuccess();
	else
	{
		Log::msgError("Failed to connect to the database.  Aborting.");
		return false;
	}
}


bool8
RouletteConnection::parseCasinoLogin(int32 stdReq, int32* Err)
{
	Info* info;
	int32 i;

	if (msgbuf[1]	!= APP1				|| msgbuf[2] != APP2 ||
		msgbuf[3]	!= PROTO_VERSION1	|| msgbuf[4] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c %d.%d",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

	info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	info->tableID = msgbuf[5];

	i = stdReq;
		
	char login[PIDLEN + 1];

	memcpy(login, msgbuf + i, PIDLEN);
	login[PIDLEN] = '\0';
	strlwr(login);
	i += PIDLEN;

//	info->pid = login;

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{			
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}

	if (tracker)
	{
		int32 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	info->pid = info->login;

	/*
	if (tracker)
	{
		int32 result = RouletteSQL::ExecFailed;

		tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		tracker->unlock();

		if (result != RouletteSQL::NoError)
		{
			if(result == RouletteSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == RouletteSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

            DEBUGMSG(("Login: attempt to play without logging in: %S",
					(const uint16*) info->login));
            delete info;
            return false;
		}
	}
	*/

	casinoSpecific = info;

	return true;
}

bool8
RouletteConnection::parseUnicodeCasinoLogin(int32 stdReq, int32* Err)
{
	Info* info;
	int32 i;

	if (msgbuf[2]	!= APP1				|| msgbuf[3] != APP2 ||
		msgbuf[4]	!= PROTO_VERSION1	|| msgbuf[5] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c %d.%d",
				msgbuf[2],msgbuf[3],msgbuf[4],msgbuf[5]));
		return false;
	}

	info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	info->tableID = msgbuf[6];

	i = stdReq;

	i += info->login.deserialize(msgbuf + i);
	info->login.toLower();

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{		
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}

	if (tracker)
	{
		int32 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	info->pid = info->login;

	/*
	if (tracker)
	{
		int32 result = RouletteSQL::ExecFailed;

		tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		tracker->unlock();

		if (result != RouletteSQL::NoError)
		{
			if(result == RouletteSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == RouletteSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

            DEBUGMSG(("Login: attempt to play without logging in: %S",
					(const uint16*) info->login));
            delete info;
            return false;
		}
	}
	*/

	casinoSpecific = info;

	return true;
}

void
RouletteConnection::removeCasinoSpecific()
{
	delete (pInfo)casinoSpecific;

	casinoSpecific = (void*)0;
}

int32
RouletteConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
RouletteConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

bool8
RouletteConnection::trackGetBalance( int32 *balance )
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = RouletteSQL::ExecFailed;
	int32 comp;
	*balance = 0;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->getBalance(ServerOpts::casinoId,
			info->pid, info->forMoney, balance, &comp);
		tracker->unlock();
	}

	return (bool8)(result == RouletteSQL::NoError);
}

int32
RouletteConnection::trackAdjustBalance(int32 Amount)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = RouletteSQL::ExecFailed;
	int32 bal=0, comp=0;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->adjustBalance(ServerOpts::casinoId,
			info->pid, info->forMoney,
			Amount, &bal, &comp);
		tracker->unlock();
	}

	if (result == RouletteSQL::NoError)
		return bal;
	return 0;
}

bool8
RouletteConnection::trackSpin(uint8 spinresult, int32 payout,
	RouletteRound *round, int32* balance)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = RouletteSQL::ExecFailed;
	int32 comp;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->spin(ServerOpts::casinoId,
			info->pid, info->tableID, info->forMoney,
			info->sessionId, info->forMoney ? compFactor : 0,
			spinresult, payout, round, balance, &comp);
		tracker->unlock();
	}

	return (bool8)(result == RouletteSQL::NoError);
}



bool8
RouletteConnection::trackGetPlayerLimits(int32* mininside, int32* maxinside,
	int32* minoutside, int32* maxoutside, int32* maxsingleinside, int32* compFactor)
{
	pInfo info = (pInfo)casinoSpecific;
	int32 result = RouletteSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->getPlayerLimits(info->pid, info->tableID,
			mininside, maxinside, minoutside, maxoutside, maxsingleinside, compFactor);
		tracker->unlock();
	}

	return (bool8)(result == RouletteSQL::NoError);
}

uint8
RouletteConnection::getTableID()
{
	pInfo info = (pInfo)casinoSpecific;

	return info ? info->tableID : 0;
}

String
RouletteConnection::getPid()
{
	pInfo info = (pInfo)casinoSpecific;

	return info ? info->login : String::emptyString;
}

void
RouletteConnection::trackaddBadPacket(int32 error_reason, char MessageType, char *msgbuf, int32 msglen)
{
	pInfo info = (pInfo)casinoSpecific;

	if (tracker && info)
	{
		tracker->lock();
		tracker->addBadPacket(info->tableID, 0x00, sBadPacketMessages[error_reason].message, sBadPacketMessages[error_reason].severity, info->pid, MessageType, (uint8*)msgbuf, msglen);
		tracker->unlock();
	}
}

int32
RouletteConnection::trackValidateLogin()
{
    pInfo info = (pInfo)casinoSpecific;
    int32 result = RouletteSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId, info->pid);
        tracker->unlock();
    }

    return result;
}

int32
RouletteConnection::trackValidateSession()
{
    pInfo info = (pInfo)casinoSpecific;
    int32 result = RouletteSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateSession(info->pid, info->sessionId);
        tracker->unlock();
    }

    return result;
}

void RouletteConnection::trackLogoutSession()
{
    pInfo info = (pInfo)casinoSpecific;

	if (tracker && info && !info->login.isEmpty())
    {
        tracker->lock();
		tracker->logout(ServerOpts::casinoId, info->login, info->forMoney, String::emptyString, info->sessionId);
        tracker->unlock();
    }
}

#else	// USE_SQL

#include <string.h>

#include "lib.h"
#include "roulserv.h"
#include "roulconn.h"
#include "roulver.h"
#include "stdncerr.h"

void RouletteServer::startTracker() { }
void RouletteServer::stopTracker() { }
bool8 RouletteServer::initSuccess() { return Server::initSuccess(); }
void RouletteConnection::trackaddBadPacket(int32 error_reason, char MessageType, char *msgbuf, int32 msglen) {}


bool8
RouletteConnection::parseCasinoLogin(int32 /*stdReq*/, int32* Err)
{
	if (msgbuf[1]	!= APP1				|| msgbuf[2] != APP2 ||
		msgbuf[3]	!= PROTO_VERSION1	|| msgbuf[4] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		return false;
	}

	casinoSpecific = (void*)100000;		//balance!

	return true;
}
void RouletteConnection::removeCasinoSpecific() { }

int32
RouletteConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
RouletteConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
RouletteConnection::trackGetBalance()
{
	return (int32)casinoSpecific;
}

int32
RouletteConnection::trackAdjustBalance(int32 Amount)
{
	casinoSpecific = (void*)((int32)casinoSpecific + Amount);
	return (int32)casinoSpecific;
}

bool8
RouletteConnection::trackGetPlayerLimits(int32* mininside, int32* maxinside,
	int32* minoutside, int32* maxoutside)
{
	*mininside = 100;
	*maxinside = 50000;
	*minoutside = 100;
	*maxoutside = 20000;

	return true;
}

bool8
RouletteConnection::trackSpin(uint8 spinresult, int32 payout,
	RouletteRound *round, int32* balance)
{
	*balance = trackAdjustBalance(payout);
	return true;
}

#endif
